#pragma once

#include <string>
#include <functional>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <pthread.h>
#include "log.hpp"
#include "thread_pool.hpp"
#include "Task.hpp"
#include "daemon.hpp"

const int default_sockfd = -1;
const std::string default_ip = "0.0.0.0";
const uint16_t default_port = 8080;
const int backlog = 10;
const size_t ip4_size = 20;

extern Log lg;

enum Error
{
    Use_error = 1,
    Creat_error,
    Bind_error,
    Listen_error
};

class tcp_serve;

struct thread_info
{
    int _sockfd;
    tcp_serve *_ptsv;
    std::string _ip;
    uint16_t _port;
    thread_info(int sockfd, tcp_serve *ptsv, std::string ip, uint16_t port)
        : _sockfd(sockfd), _ptsv(ptsv), _ip(ip), _port(port)
    {
    }
};

class tcp_serve
{
public:
    tcp_serve(const uint16_t port = default_port, const std::string ip = default_ip)
        : _listen_sockfd(-1), _is_running(false), _ip(ip), _port(default_port), _trdp(nullptr)
    {
    }

    void init()
    {

        // 1. 创建套接字
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        int opt = 1;
        // 解决服务器无法立即重新启动的问题
        setsockopt(_listen_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);
        if (_listen_sockfd < 0)
        {
            lg(Fatal, "socket create error, errno:%d, errstr:%s", errno, strerror(errno));
            exit(Creat_error);
        }
        lg(Info, "sockfd create success!");
        // 2. bind sockfd
        struct sockaddr_in serve_info;
        serve_info.sin_family = AF_INET;
        serve_info.sin_addr.s_addr = inet_addr(_ip.c_str());
        serve_info.sin_port = htons(_port);
        if (bind(_listen_sockfd, (const sockaddr *)&serve_info, sizeof serve_info) < 0)
        {
            lg(Fatal, "sockfd bind error, errno:%d, errstr:%s", errno, strerror(errno));
            exit(Bind_error);
        }
        lg(Info, "sockfd bind success!");
        // 3. tcp和udp不同的地方是服务器建立链接是被动的，需要等待有人来连接，所以需要进行listen监听
        if (listen(_listen_sockfd, backlog) < 0)
        {
            lg(Fatal, "sockfd listen error, errno:%d, errstr:%s", errno, strerror(errno));
            exit(Listen_error);
        }
        lg(Info, "socket listen success!");
    }

    // static void *routine(void *args)
    // {
    //     pthread_detach(pthread_self());
    //     thread_info *tinfo = static_cast<thread_info *>(args);
    //     auto tsp = tinfo->_ptsv;
    //     tsp->service(tinfo->_sockfd, tinfo->_ip, tinfo->_port);
    //     // 记得清理资源
    //     close(tinfo->_sockfd);
    //     delete tinfo;
    //     return nullptr;
    // }

    void start()
    {
        lg.change_record_op(diverse_file);
        // signal(SIGPIPE, SIG_IGN);
        deamon();
        _trdp = thread_pool<task>::get_instance();
        _trdp->start();
        _is_running = true;
        // signal(SIGCHLD, SIG_IGN);
        // 1. 设置ip地址以及端口号
        struct sockaddr_in serve;
        serve.sin_family = AF_INET;
        if (inet_aton(_ip.c_str(), &serve.sin_addr) == 0)
            lg(Fatal, "string ip convert to net error, ip:%s", _ip.c_str());
        serve.sin_port = htons(_port);
        // 2. 接收新链接
        for (;;)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof client;
            int sockfd = accept(_listen_sockfd, (sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                // sockfd为-1的情况
                // 1. 套接字未绑定/监听   2. 函数被信号中断
                // 3. 无效的套接字  4. 资源不足  5. 连接被重置
                lg(Warning, "tcp accept error, errno: %d, errstr:%s", errno, strerror(errno));
                errno = 0;
                continue;
            }

            // 获取新链接成功
            char client_ip[ip4_size];
            inet_ntop(AF_INET, &(client.sin_addr), client_ip, sizeof client_ip - 1);
            int client_port = ntohs(client.sin_port);
            lg(Info, "[%s:%d] connect to server successfully!", client_ip, client_port);

            // 服务代码各个版本

            // version1 -- 单进程 / 导致只有一个客户端能连接上服务
            // service(sockfd, client_ip, client_port, client);
            // close(sockfd);

            // version2 -- 多进程
            // pid_t id = fork();
            // if (id == 0)
            // {
            //     // child -- 负责使用sockfd进行通信
            //     close(_listen_sockfd);
            //     if (fork() > 0)
            //         exit(0);
            //     service(sockfd, client_ip, client_port, client);
            //     close(sockfd);
            //     // 子进程执行完这部分代码就应该推出了，不能执行后面的代码
            //     exit(0);
            // }
            // // parent
            // // 父进程不需要使用sockfd，所以可以关闭该文件描述符
            // close(sockfd);
            // // 如果使用waitpid,主进程仍然会阻塞在这里，无法进行下一次的accept，如何解决？？
            // // 1. 可以将子进程的任务交给孙子进程，然后退出孙子进程，相当于将孙子进程托孤给 OS
            // // 2. 利用信号的方法，将SIGCHILD信号的处理方式改为SIG_IGN, 这样子进程在退出的时候就会调用waitpid()
            // waitpid(id, nullptr, 0);

            // 使用多进程的代价相对于线程大的多，并且当前两个任务独立性并不高，所以使用多线程的方法
            // version3 -- 多线程
            // pthread_t c;
            // thread_info *infot = new thread_info(sockfd, this, client_ip, client_port);
            // pthread_create(&c, nullptr, routine, infot);
            // 对于主线程，需要进行join，但是这又会导致阻塞，解决方法就是将子线程和主线程detach
            // 1. 长服务不合理 2. 线程数量应该设置一定上限，防止服务器负载太高 3. 连接成功后再创建线程效率太低

            // version4 -- 进程池
            _trdp->push(task(sockfd, client_ip, client_port));
        }
    }

    ~tcp_serve()
    {
        if (_listen_sockfd > 0)
            close(_listen_sockfd);
    }

    // echo服务器服务代码
    // void service(int sockfd, const std::string client_ip, const uint16_t client_port)
    // {
    //     char message[msg_size];
    //     while (true)
    //     {
    //         // 由于accept返回的是一个文件描述符，所以可以利用文件操作接口来进行读取信息
    //         ssize_t n = read(sockfd, message, sizeof message - 1);
    //         if (n > 0)
    //         {
    //             // 在传输过程中并没有将字符串结尾的'\0'也一起传输
    //             message[n] = '\0';
    //             std::string response = "serve echo# ";
    //             response += message;
    //             n = write(sockfd, response.c_str(), response.size());
    //             if (n < 0)
    //             {
    //                 lg(Warning, "write to [%s:%d] error, errno:%s, errstr:%d", client_ip.c_str(),
    //                    client_port, errno, strerror(errno));
    //                 break;
    //             }
    //         }
    //         else if (n == 0)
    //         {
    //             // 当客户端关闭时，read不会继续阻塞，并且返回0
    //             lg(Info, "%s client disconnect", client_ip.c_str());
    //             break;
    //         }
    //         else if (n < 0)
    //         {
    //             lg(Warning, "client read error, errno:%d, errstr:%s", errno, strerror(errno));
    //             errno = 0;
    //             break;
    //         }
    //     }
    // }

private:
    int _listen_sockfd;
    bool _is_running;
    std::string _ip;
    uint16_t _port;
    thread_pool<task> *_trdp;
};